module ActionDispatch
  class Request
    include SimpleHmac::Helper

    def hmac_api_id(auth_prefix='\w+')
      result = parse_hmac(auth_prefix)
      result && result[1]
    end

    def hmac_valid?(api_secret, options={})
      options         = { timeout_seconds: 900, auth_prefix: '\w+' }.merge(options)
      timeout_seconds = options.delete :timeout_seconds
      auth_prefix     = options.delete :auth_prefix
      result          = parse_hmac(auth_prefix)
      result &&
          ((Time.now.utc - Time.httpdate(timestamp).utc < timeout_seconds) rescue false) &&
          result[2] == hmac_token(request_method, content_type, calculate_content_md5, url, timestamp, api_secret, options)
    end

    private

    def parse_hmac(auth_prefix)
      Regexp.new("\\A#{auth_prefix} ([^:]+):(.+)\\Z").match(authorization_header)
    end

    def calculate_content_md5
      (post? || put? || patch?) ? Digest::MD5.base64digest(raw_post) : ''
    end

    def content_type
      find_header(%w(CONTENT-TYPE CONTENT_TYPE HTTP_CONTENT_TYPE))
    end

    def timestamp
      find_header(%w(DATE HTTP_DATE))
    end

    def authorization_header
      find_header %w(AUTHORIZATION HTTP_AUTHORIZATION)
    end

    def find_header(keys)
      cap_env = Hash[env.each_pair { |k, v| [k.to_s.upcase, v] }]
      keys.each { |k| return cap_env[k] unless cap_env[k].blank? }
      ''
    end
  end
end